package com.zoe.onelink.dubbo.filter;

import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.StringJoiner;

import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;

/**
 * <p>标题: 参数验证过滤器</p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-08-04
 */
@Activate(group = {CONSUMER, PROVIDER}, value = VALIDATION_KEY, order = -2000)
@Slf4j
public class ParamValidationFilter implements Filter {

    public static final String ERROR_DELIMITER = ",";
    public static final String INNER_CLASS_SIGN = "$";

    private Validation validation;

    /**
     * 设置ValidationFilter的验证实例
     *
     * @param validation dubbo框架基于“validation”属性值注入的验证实例,不能使用构造器或者@Autowiread方式注入
     */
    public void setValidation(Validation validation) {
        this.validation = validation;
    }

    /**
     * 在调用实际方法之前，根据validation属性值执行验证。
     *
     * @param invoker    调用者
     * @param invocation 调用参数
     * @return .
     * @throws RpcException 如果验证失败或发生任何其他运行时异常,则抛出RpcException。
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 校验器不为空且不是内部类
        if (validation != null && !invocation.getMethodName().startsWith(INNER_CLASS_SIGN)
                && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
            try {
                Validator validator = validation.getValidator(invoker.getUrl());
                if (validator != null) {
                    validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
                }
                // RPC异常直接抛出
            } catch (RpcException e) {
                throw e;
                // 参数验证异常
            } catch (ConstraintViolationException e) {
                StringJoiner strJoiner = new StringJoiner(ERROR_DELIMITER);
                e.getConstraintViolations().forEach(validation -> strJoiner.add(validation.getMessage()));
                return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(strJoiner.toString()), invocation);
                // 校验异常基类
            } catch (ValidationException e) {
                // 只使用异常消息来避免潜在的序列化问题
                return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(e.getMessage()), invocation);
            } catch (Throwable t) {
                return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
            }
        }
        return invoker.invoke(invocation);
    }

}
